package com.gitee.walmart;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gitee.AuthClient;
import com.gitee.walmart.dto.WalmartOrdersDTO;
import com.gitee.walmart.vo.WalmartOrder;
import com.gitee.walmart.vo.WalmartOrders;
import com.gitee.walmart.vo.WalmartToken;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.http.*;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;

/**
 * Created by EalenXie on 2022/3/16 14:02
 */
public class WalmartClient implements AuthClient {


    private final ObjectMapper mapper;


    private final RestOperations restOperations;

    /**
     * Amazon selling partner(SP) Host地址 https://sellingpartnerapi-na.amazon.com
     */
    private static final String HOST = "https://marketplace.walmartapis.com";

    private static final String WM_SVC_NAME = "WM_SVC.NAME";

    private static final String WM_QOS_CORRELATION_ID = "WM_QOS.CORRELATION_ID";

    private static final String WM_SEC_ACCESS_TOKEN = "WM_SEC.ACCESS_TOKEN";


    public WalmartClient() {
        this(new RestTemplate());
    }

    public WalmartClient(RestOperations restOperations) {
        this(new ObjectMapper(), restOperations);
    }

    public WalmartClient(ObjectMapper objectMapper, RestOperations restOperations) {
        this.mapper = objectMapper;
        this.restOperations = restOperations;
    }


    /**
     * 获取accessToken
     * https://developer.walmart.com/api/us/mp/auth#operation/tokenAPI
     *
     * @param clientId     客户端Id
     * @param clientSecret 客户端密钥
     * @return 访问令牌
     */
    public ResponseEntity<WalmartToken> accessToken(String clientId, String clientSecret) {
        HttpHeaders headers = getCommonHeaders(clientId, clientSecret);
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        return restOperations.exchange(URI.create(String.format("%s/v3/token?grant_type=client_credentials", HOST)), HttpMethod.POST, new HttpEntity<>(null, headers), WalmartToken.class);
    }

    /**
     * 获取公共的请求头
     */
    public HttpHeaders getCommonHeaders(String clientId, String clientSecret) {
        HttpHeaders headers = getBasicHeaders(clientId, clientSecret);
        headers.set(WM_SVC_NAME, "Walmart Marketplace");
        headers.set(WM_QOS_CORRELATION_ID, UUID.randomUUID().toString());
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        return headers;
    }


    /**
     * https://developer.walmart.com/api/us/mp/orders#operation/getAllReleasedOrders
     */
    public ResponseEntity<WalmartOrders> orders(String clientId, String clientSecret, String accessToken, WalmartOrdersDTO dto) {
        HttpHeaders headers = getCommonHeaders(clientId, clientSecret);
        headers.set(WM_SEC_ACCESS_TOKEN, accessToken);
        @SuppressWarnings("unchecked") Map<String, ?> args = mapper.convertValue(dto, Map.class);
        return restOperations.exchange(String.format("%s/v3/orders", HOST), HttpMethod.GET, new HttpEntity<>(null, headers), WalmartOrders.class, args != null ? args : new LinkedHashMap<>());
    }


    /**
     * https://developer.walmart.com/api/us/mp/orders#operation/getAnOrder
     */
    public ResponseEntity<WalmartOrder> order(String clientId, String clientSecret, String accessToken, String orderId) {
        HttpHeaders headers = getCommonHeaders(clientId, clientSecret);
        headers.set(WM_SEC_ACCESS_TOKEN, accessToken);
        return restOperations.exchange(URI.create(String.format("%s/v3/orders/%s", HOST, orderId)), HttpMethod.GET, new HttpEntity<>(null, headers), WalmartOrder.class);
    }

    /**
     * 生成签名字符串
     *
     * @param consumerId 消费者 ID
     * @param baseUrl    API 调用的 URL
     * @param httpMethod API 调用的请求方法（全部大写 ）
     * @param timestamp  当前 Unix 纪元时间戳
     * @return 签名字符串
     */
    public static String getSignStr(String consumerId, String baseUrl, String httpMethod, String timestamp) {
        return consumerId + "\n" + baseUrl + "\n" + httpMethod.toUpperCase() + "\n" + timestamp + "\n";
    }


    /**
     * 签名
     *
     * @param signStr    签名字符串
     * @param privateKey 签名key
     * @return 数字签名
     */
    public static String sign(String signStr, String privateKey) {
        String signatureString = null;
        try {
            byte[] encodedKeyBytes = Base64.decodeBase64(privateKey);
            PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(encodedKeyBytes);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            PrivateKey myPrivateKey = kf.generatePrivate(privSpec);
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(myPrivateKey);
            byte[] data = signStr.getBytes(StandardCharsets.UTF_8);
            signature.update(data);
            byte[] signedBytes = signature.sign();
            signatureString = Base64.encodeBase64String(signedBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return signatureString;
    }

}
